package streamforce.crypto.owner;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import streamforce.crypto.KPAbe;
import streamforce.crypto.event.Ciphertext;
import streamforce.crypto.event.StockEvent;

/**
 * Helper class for EPOwner. It encrypts the input tuple
 * and returns an array of type StockEvent
 *
 */
public class DataEncryptor {
	private final Log log = LogFactory.getLog(DataEncryptor.class);
	KPAbe cryptoHandler;
	int streamId; 
	//state used for Aggregate 2
	Map<Integer,Integer> windowSumPrice, windowSumVolume; 
	int[] windowSizes; 
	
	public DataEncryptor(int streamId, KPAbe ch, int[] ws){
		this.cryptoHandler = ch;
		this.streamId = streamId; 
		windowSumPrice = new HashMap<Integer,Integer>();
		windowSumVolume = new HashMap<Integer,Integer>();
		windowSizes = ws;
		for (int i=0; i<windowSizes.length; i++){
			windowSumPrice.put(windowSizes[i], 0);
			windowSumVolume.put(windowSizes[i], 0);
		}	
	}
	
	//add deterministic encryption to existing encrypted tuple
	public void encrypt_join_price(StockEvent se, int value) {
		byte[] detPrice = this.cryptoHandler.enc_det(se.getStockId(), value);
		se.getPrice().put("price_det", new Ciphertext(detPrice));		
	}
	
	public void encrypt_join_volume(StockEvent se, int value) {
		byte[] detVolume = this.cryptoHandler.enc_det(se.getStockId(), value);
		se.getVolume().put("volume_det", new Ciphertext(detVolume));		
	}
	
	//no window
	public StockEvent encrypt_simple(int timestamp, int hour, 
			int stockId, int price, int volume){
		StockEvent se = new StockEvent(timestamp, hour, stockId);
				
		long start = System.currentTimeMillis();
		
		String[] attributes = new String[4];
		attributes[0] = "timestamp = "+timestamp;
		attributes[1] = "hour = "+hour; 
		attributes[2] = "stockId_"+stockId;
		attributes[3] = "att_price";
		se.getPrice().put(
				"ws=1",
				new Ciphertext(cryptoHandler.enc_simple(stockId, attributes,
						price, timestamp)));				
		attributes[3] = "att_volume";
		se.getVolume().put(
				"ws=1",
				new Ciphertext(cryptoHandler.enc_simple(stockId, attributes,
						volume, timestamp)));		
		long end = System.currentTimeMillis();		
		log.info("encryption time = "+(end-start));		
		return se;
	}
	
	public StockEvent encrypt_Aggregate1(int timestamp, int hour, int stockId,
			int price, int volume) {
		StockEvent se = new StockEvent(timestamp, hour, stockId);
		long start = System.currentTimeMillis();

		String[] attributes = new String[5];
		attributes[0] = "timestamp = " + timestamp;
		attributes[1] = "hour = "+hour; 
		attributes[2] = "stockId_" + stockId;
		attributes[3] = "att_price";
		attributes[4] = "ws_1"; 
		byte[] ciphertext;
		se.getPrice().put(
				"ws=1",
				new Ciphertext(cryptoHandler.enc_simple(stockId, attributes,
						price, timestamp)));
		for (int i = 0; i < windowSizes.length; i++) {
			attributes[4] = "ws_"+windowSizes[i];
			ciphertext = cryptoHandler.enc_slidingWindow_Aggregate1(stockId, 0,
					windowSizes[i], attributes, price, timestamp);
			se.getPrice().put("ws=" + windowSizes[i],
					new Ciphertext(ciphertext));
		}

		attributes[3] = "att_volume";
		attributes[4] = "ws_1";
		se.getVolume().put(
				"ws=1",
				new Ciphertext(cryptoHandler.enc_simple(stockId, attributes,
						volume, timestamp)));
		for (int i = 0; i < windowSizes.length; i++) {
			attributes[4] = "ws_"+windowSizes[i]; 
			ciphertext = cryptoHandler.enc_slidingWindow_Aggregate1(stockId, 1,
					windowSizes[i], attributes, volume, timestamp);
			se.getVolume().put("ws=" + windowSizes[i],
					new Ciphertext(ciphertext));
		}
		long end = System.currentTimeMillis();

		log.info("encryption time = " + (end - start));		
		return se;
	}
	
	//owner to encrypt these
	public StockEvent encrypt_Aggregate2(int timestamp, int hour, int stockId,
			int price, int volume) {
		StockEvent se = new StockEvent(timestamp, hour, stockId);
		long start = System.currentTimeMillis();

		String[] attributes = new String[5];
		attributes[0] = "timestamp = " + timestamp;
		attributes[1] = "hour = "+hour; 
		attributes[2] = "stockId_" + stockId;
		attributes[3] = "att_price";
		attributes[4] = "ws_1"; 
		int currentSum;
		byte[] ciphertext;
		se.getPrice().put(
				"ws=1",
				new Ciphertext(cryptoHandler.enc_simple(stockId, attributes,
						price, timestamp)));
				
		//updates current sums and adds new ciphertexts if applicable
		for (int i = 0; i < windowSizes.length; i++) {
			currentSum = this.windowSumPrice.get(windowSizes[i])+price;			
									
			if (timestamp % windowSizes[i] == (windowSizes[i]-1)) {
				attributes[4]= "ws_"+windowSizes[i]; 
				ciphertext = cryptoHandler.enc_simple(stockId, attributes,
						currentSum, timestamp); 				
				se.getPrice().put("ws=" + windowSizes[i],
						new Ciphertext(ciphertext));
				this.windowSumPrice.put(windowSizes[i], 0);
			}
			else
				this.windowSumPrice.put(windowSizes[i], currentSum);
		}

		attributes[3] = "att_volume";
		attributes[4] = "ws_1"; 
		se.getVolume().put(
				"ws=1",
				new Ciphertext(cryptoHandler.enc_simple(stockId, attributes,
						volume, timestamp)));
		for (int i = 0; i < windowSizes.length; i++) {
			currentSum = this.windowSumVolume.get(windowSizes[i])+volume;	
			if (timestamp % windowSizes[i] == (windowSizes[i]-1)) {
				attributes[4] = "ws_"+windowSizes[i]; 
				ciphertext = cryptoHandler.enc_simple(
						stockId, attributes, currentSum,timestamp);
				se.getVolume().put("ws=" + windowSizes[i],
						new Ciphertext(ciphertext));
				this.windowSumVolume.put(windowSizes[i], 0);
			}
			else
				this.windowSumVolume.put(windowSizes[i], currentSum);
		}
		long end = System.currentTimeMillis();

		log.info("encryption time = " + (end - start));		
		return se;		
	}
	
	//Aggregate-3 encryption
	public StockEvent encrypt_Aggregate3(int timestamp, int hour, int stockId,
			int price, int volume) {
		StockEvent se = new StockEvent(timestamp, hour, stockId);
		long start = System.currentTimeMillis();

		String[] attributes = new String[5];
		attributes[0] = "timestamp = " + timestamp;
		attributes[1] = "hour = "+hour; 
		attributes[2] = "stockId_" + stockId;
		attributes[3] = "att_price";
		attributes[4] = "ws_1";
		byte[] ciphertext;
		se.getPrice().put(
				"ws=1",
				new Ciphertext(cryptoHandler.enc_simple(stockId, attributes,
						price, timestamp)));

		// now encrypt with ws_x attribute
		attributes[4] = "ws_x"; 		
		int[] lens = new int[2];
		ciphertext = cryptoHandler.enc_slidingWindow_Aggregate3(stockId,0,
				attributes, attributes, price, timestamp, lens);
		Ciphertext ct = new Ciphertext(ciphertext, lens);
		se.getPrice().put("ws=x", ct);
		
		attributes[3] = "att_volume";
		attributes[4] = "ws_1";
		se.getVolume().put(
				"ws=1",
				new Ciphertext(cryptoHandler.enc_simple(stockId, attributes,
						volume, timestamp)));
		
 		attributes[4] = "ws_x";		
		lens = new int[2];
		ciphertext = cryptoHandler.enc_slidingWindow_Aggregate3(stockId,1,
				attributes, attributes, volume, timestamp, lens);
		ct = new Ciphertext(ciphertext, lens);
		se.getVolume().put("ws=x", ct);
		long end = System.currentTimeMillis();

		log.info("encryption time = " + (end - start));		
		return se;		
	}
}
